package com.yuda.tools.checkanswer.service.impl;

import com.aliyun.oss.OSS;
import com.aliyun.oss.model.GetObjectRequest;
import com.aliyun.oss.model.SimplifiedObjectMeta;
import com.yuda.common.core.utils.StringUtils;
import com.yuda.tools.checkanswer.Oss.OssMap;
import com.yuda.tools.checkanswer.dto.*;
import com.yuda.tools.checkanswer.mapper.CheckAnswerMapper;
import com.yuda.tools.checkanswer.service.CheckAnswerService;
import com.yuda.tools.checkanswer.service.CheckLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.sql.*;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

@Service
public class CheckAnswerServiceImpl implements CheckAnswerService {

    @Autowired
    private CheckAnswerMapper checkAnswerMapper;

    @Autowired
    private CheckLogService checkLogService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Value("${check.answerDbPath}")
    private String answerDbPath;

    @Value("${check.answerDownLoadPath}")
    private String answerDownLoadPath;

    @Value("${check.oss_inner_out}")
    private String ossInnerOrOut;

    int checkThreadNum = 20;

    private final ExecutorService threadPool = Executors.newWorkStealingPool(checkThreadNum);

    @Value("${check.prefix}")
    private String prefix;

    private final Map<Integer, Integer> quesCountMap = new HashMap<>();

    //根据tese_session subjectIndex 得到科目
    private final Map<String, String> subjectMap = new HashMap<>();

    private Integer currentTestSession = 0;

    private Integer currentSubjectIndex = 0;

    AtomicInteger atomicInteger = new AtomicInteger();

    @Override
    public String checkAnswer(int testSession, int subjectIndex) throws InterruptedException {

        atomicInteger.set(0);
        //装填quesCountMap
        if (!fillQuesCountMap()) {
            return "ques_count 没有准备好";
        }

        fillSubjectMap();

        File file = new File(answerDownLoadPath);
        if (!file.isDirectory()) {
            file.mkdirs();
        }
        List<StudentOssDto> studentOssDtos = this.listStudentsForCheck(testSession, subjectIndex);
        currentTestSession = testSession;
        currentSubjectIndex = subjectIndex;

        for (StudentOssDto studentOssDto : studentOssDtos) {
            threadPool.execute(() -> {
                System.out.println("正在处理第 -> " + atomicInteger.incrementAndGet());
                doCheckAnswer(studentOssDto, testSession, subjectIndex);
            });
        }
        int size = studentOssDtos.size();

        while (atomicInteger.get() < size) {
            TimeUnit.SECONDS.sleep(1);
        }

        System.out.println("执行完毕...");
        return "ok";
    }

    private void fillSubjectMap() {
        List<SubjectMap> list = checkAnswerMapper.listSubjectTssi();
        for (SubjectMap sm : list) {
            subjectMap.put(sm.getTssi(), sm.getSubjectName());
        }
    }


    private void doCheckAnswer(StudentOssDto studentOssDto, int testSession, int subjectIndex) {
        OSSArea ossArea = studentOssDto.getOssArea();
        OSSManageArea ossManageArea = studentOssDto.getOssManageArea();
        String dbKey = answerDbPath + studentOssDto.getStudentId() + "_" + studentOssDto.getPaperId() + "_" + studentOssDto.getSubjectId() + "_" + studentOssDto.getPcNo() + ".db";
        String dbManageKey = answerDbPath + studentOssDto.getStudentId() + "_" + studentOssDto.getPaperId() + "_" + studentOssDto.getSubjectId() + "_" + studentOssDto.getPcNo() + "_manage.db";

        Map<String, String[]> map = new LinkedHashMap<>(4);
        if (ossArea.getBucketName().equals(ossManageArea.getManageBucketName())) {
            map.put(ossArea.getBucketName(), new String[]{dbKey, dbManageKey});
        } else {
            map.put(ossArea.getBucketName(), new String[]{dbKey});
            map.put(ossManageArea.getManageBucketName(), new String[]{dbManageKey});
        }

        String ossDefault = "defanswer";
        map.put(ossDefault, new String[]{dbKey, dbManageKey});

        //设置答案的真实区域和文件名称 并返回是否存在
        boolean flag = setRealAnswer(studentOssDto, map);

        if (flag) {
            OSS oss = OssMap.get(studentOssDto.getRealArea() + ossInnerOrOut.trim());
            oss.getObject(new GetObjectRequest(studentOssDto.getRealArea(), studentOssDto.getRealFileName()), new File(answerDownLoadPath + "/" + studentOssDto.getRealFileName().replaceAll(answerDbPath, "")));
            boolean ok = doCheck(studentOssDto, testSession, subjectIndex);
            if (ok) {
                checkAnswerMapper.updateAnswerStatusOk(studentOssDto.getRealArea(), studentOssDto.getRealFileName().replaceAll(answerDbPath, ""), studentOssDto.getStudentId(), testSession, subjectIndex, "文件正常");
                stringRedisTemplate.opsForValue().set(prefix + ":" + studentOssDto.getPaperId() + ":" + studentOssDto.getSubjectId() + ":" + studentOssDto.getStudentId(), "1");
                stringRedisTemplate.opsForSet().add(prefix + ":" + studentOssDto.getPaperId() + ":" + studentOssDto.getSubjectId(), studentOssDto.getStudentId());
            }
        }
    }

    private boolean setRealAnswer(StudentOssDto studentOssDto, Map<String, String[]> map) {

        for (String bucket : map.keySet()) {
            String[] v = map.get(bucket);
            for (String key : v) {
                boolean exist = ossExist(OssMap.get(bucket + ossInnerOrOut.trim()), bucket, studentOssDto.getExamStartTime(), key);
                if (exist) {
                    studentOssDto.setRealArea(bucket);
                    studentOssDto.setRealFileName(key);
                    return true;
                }
            }
        }
        return false;
    }

    //    @Check
    @Override
    public Map<Integer, Map<Integer, Integer>> getSubjectQuesCount() {
        return checkAnswerMapper.getSubjectQuesCount();
    }

    public List<StudentOssDto> listStudentsForCheck(int testSession, int subjectIndex) {
        return checkAnswerMapper.listStudentsForCheck(testSession, subjectIndex);
    }

    private boolean ossExist(OSS oss, String bucketName, int examStartTime, String key) {
        boolean exist = oss.doesObjectExist(bucketName, key);

        //检验文件是不是考试之后才有的
        if (exist) {
            SimplifiedObjectMeta simplifiedObjectMeta = oss.getSimplifiedObjectMeta(bucketName, key);
            if (examStartTime >= simplifiedObjectMeta.getLastModified().getTime() / 1000) {
                exist = false;
            }
        }
        return exist;
    }


    private boolean doCheck(StudentOssDto studentOssDto, int testSession, int subjectIndex) {

        Connection c = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {

            Class.forName("org.sqlite.JDBC");
            c = DriverManager.getConnection("jdbc:sqlite:" + answerDownLoadPath + "/" + studentOssDto.getRealFileName().replaceAll(answerDbPath, ""));
            System.out.println(answerDownLoadPath + "/" + studentOssDto.getRealFileName().replaceAll(answerDbPath, ""));
            c.setAutoCommit(false);
            int count = 0;//记录了条数
            st = c.prepareStatement("select count(1) from answer where (answer_abc is null or trim(answer_abc) ='') and type not in ('5','6','7','8') ");
            rs = st.executeQuery();
            while (rs.next()) {
                count = rs.getInt(1);
            }
            if (count > 0) {
                checkAnswerMapper.updateAnswerStatus(studentOssDto.getStudentId(), testSession, subjectIndex, "答案为空的个数为" + count + "条");
                stringRedisTemplate.opsForValue().set(prefix + ":" + studentOssDto.getPaperId() + ":" + studentOssDto.getSubjectId() + ":" + studentOssDto.getStudentId(), "2");
                return false;
            }

            st = c.prepareStatement("select count(1) from answer");
            rs = st.executeQuery();

            while (rs.next()) {
                count = rs.getInt(1);
            }

            if (count == 0) { //空表
                checkAnswerMapper.updateAnswerStatus(studentOssDto.getStudentId(), testSession, subjectIndex, "文件无记录");
                stringRedisTemplate.opsForValue().set(prefix + ":" + studentOssDto.getPaperId() + ":" + studentOssDto.getSubjectId() + ":" + studentOssDto.getStudentId(), "2");
                return false;
            }

            //检查题目条数 //不等于总条数  答案为空
            int integer = quesCountMap.get(studentOssDto.getSubjectId());
            if (count != integer) {
                checkAnswerMapper.updateAnswerStatus(studentOssDto.getStudentId(), testSession, subjectIndex, "答案条数(" + count + "条)不等于总条数");
                stringRedisTemplate.opsForValue().set(prefix + ":" + studentOssDto.getPaperId() + ":" + studentOssDto.getSubjectId() + ":" + studentOssDto.getStudentId(), "2");
                return false;
            }

            //查询是不是全是-1
            st = c.prepareStatement("select count(1) from answer where answer_abc = -1");
            rs = st.executeQuery();

            while (rs.next()) {
                count = rs.getInt(1);
            }

            if (count >= integer) {
                checkAnswerMapper.updateAnswerStatus(studentOssDto.getStudentId(), testSession, subjectIndex, "全是-1");
                stringRedisTemplate.opsForValue().set(prefix + ":" + studentOssDto.getPaperId() + ":" + studentOssDto.getSubjectId() + ":" + studentOssDto.getStudentId(), "2");
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            //can not open db
            checkAnswerMapper.updateAnswerStatus(studentOssDto.getStudentId(), testSession, subjectIndex, "文件打不开");
            stringRedisTemplate.opsForValue().set(prefix + ":" + studentOssDto.getPaperId() + ":" + studentOssDto.getSubjectId() + ":" + studentOssDto.getStudentId(), "2");
            return false;
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }
                if (st != null) {
                    st.close();
                }
                if (c != null) {
                    c.close();
                }
                File file = new File(answerDownLoadPath + "/" + studentOssDto.getRealFileName().replaceAll(answerDbPath, ""));
                if (file.exists())
                    file.delete();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    private boolean fillQuesCountMap() {
        Map<Integer, Map<Integer, Integer>> subjectQuesCount = this.getSubjectQuesCount();
        for (Integer k : subjectQuesCount.keySet()) {
            Map<Integer, Integer> integerMap = subjectQuesCount.get(k);
            quesCountMap.put(integerMap.get("subjectId"), (int) integerMap.get("c"));
        }
        return !subjectQuesCount.isEmpty();
    }

    @Override
    public List<ResultMessage> listResultMessage(String searchKey, Integer[] provinceIds, Integer marked, Integer handle) {
        return checkAnswerMapper.listResultMessage(searchKey, provinceIds, marked, handle);
    }

    //考试中
    @Override
    public List<ResultMessage> listResultExaming(String searchKey, Integer[] provinceIds) {
        return checkAnswerMapper.listResultExamIng(searchKey, provinceIds);
    }

    @Override
    public int getPcNoByStudentId(String s) {
        return checkAnswerMapper.getPcNo(s);
    }

    @Override
    public String checkCanBeUpdateRoadMap() {
        return checkAnswerMapper.checkCanBeUpdateRoadMap();
    }

    @Override
    public void updateRoadMap() {
        checkAnswerMapper.updateRoadMap();
    }

    @Override
    public RoadMap roadMap() {
        return checkAnswerMapper.roadMap();
    }

    @Override
    public List<Integer> listQuestionIdsFromMarked(String studentId, String subjectId) {

        return checkAnswerMapper.listQuestionIdsFromMarked(studentId, subjectId);
    }

    @Override
    public List<Integer> listAllQuestionsBySubjectId(String subjectId) {

        return checkAnswerMapper.listAllQuestionsBySubjectId(subjectId);
    }

    @Override
    public void supplementAnswerToMarkedTable(List<FormalAnswerMarked> all) {

        checkAnswerMapper.supplementAnswerToMarkedTable(all);
    }

    @Override
    public SubjectIdAndPaperId findSubjectIdAndPaperId(String subjectRelationtId) {
        return checkAnswerMapper.findSubjectIdAndPaperId(subjectRelationtId);
    }

    @Override
    public String getZkzNumByStudentId(String studentId) {

        return checkAnswerMapper.getZkzNumByStudentId(studentId);
    }

    @Override
    public void updateTypeForMarkedTable() {

        checkAnswerMapper.updateTypeForMarkedTable();
    }


    @Override
    public List<Province> listProvince() {
        return checkAnswerMapper.listProvince();
    }

    @Transactional
    @Override
    public int updateAnswerSource(String studentId, String subjectId, int status) {

        //判断有没有被标记为正常 如果没有标记 不能走以下的逻辑
        int isMarked = checkAnswerMapper.isMarked(studentId, subjectId);
        if (isMarked == 0) return -1;

        //根据status 解析答案并存入到 异常答案表
        if (status == 2) { //从mq中取
            //从mq中插入数据
            //判断有数据
            int res = checkAnswerMapper.checkMqExistStudent(studentId, subjectId);
            if (res == 0) {
                return -4;
            }
            deleteFormalAnswerMarked(studentId, subjectId);
            moveAnswerFromMq(studentId, subjectId);
        } else if (status == 3) { //从日志中取,只有没换过考位的才取

            //判断有没有换过考位
            Integer changeCount = checkAnswerMapper.changePcNoCountBySubjectId(studentId, subjectId);
            if (changeCount > 0) return -2; //换考位的不做处理
            return moveAnswerForLog(studentId, subjectId);
        } else if (status == 4) {
            checkAnswerMapper.updateAnswerSource(studentId, subjectId, 4);
        }
        return 1;
    }

    //下载日志 并把 日志的答案 去重 整理 放到 formal answer marked 表
    private int moveAnswerForLog(String studentId, String subjectId) {
        int paperId = checkAnswerMapper.getPaperIdBySubjectId(subjectId);
        int pcNo = checkAnswerMapper.getPcNo(studentId);
        StudentLog studentLog = new StudentLog();
        studentLog.setStudentId(studentId);
        studentLog.setPcNo(pcNo);
        String maxTimeArea = checkLogService.findMaxTimeArea(studentLog, paperId);
        if (maxTimeArea.equals("")) {
            return -3;
        }
        deleteFormalAnswerMarked(studentId, subjectId);
        checkAnswerMapper.updateAnswerSource(studentId, subjectId, 3);
        checkLogService.copy(paperId, subjectId, studentLog, maxTimeArea);
        return 1;
    }

    //把mq的答案 去重 整理 放到 formal answer marked 表
    private void moveAnswerFromMq(String studentId, String subjectId) {
        checkAnswerMapper.updateAnswerSource(studentId, subjectId, 2);
        checkAnswerMapper.moveAnswerFromMq(studentId, subjectId);
    }

    //从formalAnswer异常表中先把数据删除
    public int deleteFormalAnswerMarked(String studentId, String subjectId) {
        return checkAnswerMapper.deleteFormalAnswerMarked(studentId, subjectId);
    }

    @Override
    public List<MqAnswer> searchFromMq(String studentId, String subjectId) {

//        Integer i = checkAnswerMapper.searchRealCount(subjectId);
//        Integer i1 = checkAnswerMapper.searchMqCount(studentId, subjectId);
//
//        return "本科目中题目总数：" + (i == null ? 0 : i) + "<br>" + "检测到MQ中答案总数：" + (i1 == null ? 0 : i1);

        return checkAnswerMapper.searchMqAnswer(studentId, subjectId);
    }

    @Transactional
    @Override
    public int markAsNormal(String studentId, String subjectId, Integer status) {

        //标记redis 正常
        //根据subjectId找到paperId
        int paperId = checkAnswerMapper.getPaperIdBySubjectId(subjectId);
        int changeResult;
        try {
            stringRedisTemplate.opsForValue().set(prefix + ":" + paperId + ":" + studentId + ":" + studentId, String.valueOf(status));
        } finally {
            if (status == 1) { //标记正常
                stringRedisTemplate.opsForSet().add(prefix + ":" + paperId + ":" + subjectId, studentId);
                changeResult = checkAnswerMapper.markAsNormal(studentId, subjectId);
            } else { //反标记
                changeResult = checkAnswerMapper.markAsAbnormal(studentId, subjectId);
                //把老的记录删除
                checkAnswerMapper.deleteFormalAnswerMarked(studentId, subjectId);
            }
        }
        return changeResult;
    }

    @Override
    public String currentCheckTips() {
        String s = subjectMap.get(currentTestSession + "" + currentSubjectIndex);
        if (!StringUtils.isEmpty(s)) {
            //查询当前科目应该查询的数量 和 已经查询的数量
            int total = checkAnswerMapper.getTotalTask(currentTestSession, currentSubjectIndex);
            int aready = checkAnswerMapper.getCheckedNum(currentTestSession, currentSubjectIndex);
            if (total == aready) {
                //更新sparta_paper_subject_relation
                checkAnswerMapper.updateCheckedSuccess(currentTestSession, currentSubjectIndex);
            }
            return "当前正在检查的科目是: " + s + " 应检查的人数是: " + total + " 已检查且正常人数是：" + aready;
        }
        return null;
    }


}
